package test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import com.xiaoleilu.hutool.util.StrUtil;

public class TLV {
	
	public enum MetaType {
		B, C;
	}
	
	
	private int tag;
	private byte tagbyte;// 1bit长度, 1bit压缩标志, 6bitTag
	private MetaType metatype;
	private String value;
	private int length;
	private byte[] valuebytes;
	private ByteArrayOutputStream bos;  //tlv总字节流
	
	public static String createTLV(String tag,String value){
		return StrUtil.format("{}{}{}", tag,value.getBytes(ChinaUMSZjConfig.charset).length,value);
	}
	
	public TLV(int tag, Object obj, MetaType metaType) throws Exception {
		if (obj == null || metaType == null) {
			throw new NullPointerException();
		}
		if (metaType == MetaType.B) {
			if ( obj instanceof byte[] ) {
				setMetatype(metaType);
				setTag(tag);
				setValuebytes((byte[])obj);
				
			} else {
				throw new Exception("转成byte[]错误");
			}
		} else if (metaType == MetaType.C) {
			if (obj instanceof String) {
				setMetatype(metaType);
				setTag(tag);
				setValue((String)obj);
				setValuebytes(value.getBytes(ChinaUMSZjConfig.charset));
			} else {
				throw new Exception("转成String错误");
			}
		} else {
			throw new Exception("数据类型错");
		}
	
		bos = new ByteArrayOutputStream();
		
		try {
			int len = valuebytes.length;
			if (len <= 255) {
				tagbyte = (byte) (tagbyte & 0x7F);			// 0111 1111
			} else {
				tagbyte = (byte) (tagbyte | 0x80);			// 1000 0000
			}
		
			//byte[] valueBytes = null;
			if ( IsBcdCompress()) {
				valuebytes = BcdCodeUtil.str2cbcd(valuebytes);
				setCompressBit(true);
			} else {
				setCompressBit(false);
			}
		
			tagbyte = (byte) (tagbyte | (byte)tag);
		
			bos.write(tagbyte);
			length ++;
			if ( len <= 255) {
				bos.write(valuebytes.length);
				length++;
			} else {
				byte[] intarray = new byte[2];
				intarray[0] = (byte) ((valuebytes.length >> 8) & 0xFF);
				intarray[1] = (byte) ((valuebytes.length) & 0xFF);
				bos.write(intarray, 0, 2);
				length += 2;
			}
		
			bos.write(valuebytes);
			length = valuebytes.length;
		
		} catch (Exception e) {
			throw new Exception("组装报文错误");
		} finally {
			bos.close();
		}
	}
	
	
	public boolean IsBcdCompress() {
		if ( metatype == MetaType.B ) {
			return false;
		}
		
		if (value.equals(""))
			return false;
		
		
		for (int i = 0; i < value.length(); i++) {
			if (!Character.isDigit(value.charAt(i)))
				return false;
		}
		
		if (( valuebytes[valuebytes.length - 1] & 0x0F ) == 15 ) {
			return false;
		}
		return true;
	}
	
	public void setCompressBit( boolean isCompress) {
		if (isCompress) {
			tagbyte = (byte) (tagbyte | 0x40); // 0100 0000
		} else {
			tagbyte = (byte) (tagbyte & 0xBF); // 1011 1111
		}
	}
	
	public byte[] getByteData() throws IOException {
		return bos.toByteArray();
	}
	
	public String getMacKey() throws Exception {
		int mackeylen = 8;
		byte[] macbytes = new byte[mackeylen];

		if (valuebytes.length == 40) {
			for(int i = 0; i < mackeylen; i++) {
				macbytes[i] = valuebytes[i + 20];
			}
			
		} else if (valuebytes.length == 24) {
			for(int i = 0; i < mackeylen; i++) {
				macbytes[i] = valuebytes[i + 12];
			}
		} else {
			throw new Exception("MACKEY域长度不正确");
		}
		
		return BitConvert.Hex2Str(macbytes);
	}
	
	public String getCheckValue() {
		int checkvaluelen = 4;
		byte[] checkvaluebytes = new byte[checkvaluelen];
		if (valuebytes.length == 40) {
			for(int i = 0; i < checkvaluelen; i++) {
				checkvaluebytes[i] = valuebytes[i + 36];
			}
			
		} else if (valuebytes.length == 24) {
			for(int i = 0; i < checkvaluelen; i++) {
				checkvaluebytes[i] = valuebytes[i + 20];
			}
		}
		return BitConvert.Hex2Str(checkvaluebytes);
	}


	public static void main(String[] args) throws Exception {
		TLV tlv = new TLV(6, "000331088880007", TLV.MetaType.C);
		byte[] gbd = tlv.getByteData();
		for ( int i = 0; i < gbd.length; i++) {
			if ( Integer.toHexString(0xFF & gbd[i]).length() == 1 ) {
				System.out.print( "0" + Integer.toHexString(0xFF & gbd[i]).toUpperCase() );
			} else {
				System.out.print( Integer.toHexString(0xFF & gbd[i]).toUpperCase() );
			}
		}
		System.out.println();
		System.out.println(tlv.getValue());
		//0108C7A9B5BDB3C9B9A6
		String ret = "C7A9B5BDB3C9B9A6";
		byte[] ret1 = BitConvert.Str2Hex(ret);
		System.out.println("xx" + new String(ret1));
	}

	/**
	 * @return the tag
	 */
	public int getTag() {
		return tag;
	}

	/**
	 * @param tag the tag to set
	 */
	public void setTag(int tag) {
		this.tag = tag;
	}

	/**
	 * @return the value
	 */
	public String getValue() {
		return value;
	}

	/**
	 * @param value the value to set
	 */
	public void setValue(String value) {
		this.value = value;
	}

	/**
	 * @return the length
	 */
	public int getLength() {
		return length;
	}

	/**
	 * @param length the length to set
	 */
	public void setLength(int length) {
		this.length = length;
	}
	
	public String toString() {
		return value;
	}

	public byte[] getValuebytes() {
		return valuebytes;
	}

	public void setValuebytes(byte[] valuebytes) {
		this.valuebytes = valuebytes;
	}

	public MetaType getMetatype() {
		return metatype;
	}

	public void setMetatype(MetaType metatype) {
		this.metatype = metatype;
	}
	
}
